package com.aaa.jvmtest.gcmanagement;

import java.util.ArrayList;
import java.util.List;

/**
 * GC垃圾回收分析
 *
 * @author Tu
 * @date 2022/7/17 17:07
 */
public class GCAnalyze {

    private static final int _512K = 512 * 1024;
    private static final int _1M = 1024 * 1024;
    private static final int _6M = 6 * 1024 * 1024;
    private static final int _7M = 7 * 1024 * 1024;
    private static final int _8M = 8 * 1024 * 1024;

    public static void main(String[] args) throws InterruptedException {
        //init();
        //defNewTest();
        //generationTest();
        bigObjectTactics();
    }

    /**
     * 设置默认虚拟机参数：-Xms20m   -Xmx20m    -Xmn10m    -XX:+UseSerialGC                    -XX:+PrintGCDetails   -verbose:gc
     * 堆初始大小  堆最大空间   新生代大小  使用这个垃圾回收器，避免默认动态调整比例                   打印GC详情
     * 查看打印信息，读打印信息
     */
    public static void init() {
        //打印的信息
        //9216K : 划了10m内存，显示9m，原因是默认8m分给伊甸园，from和to各占1m。在计算幸存区的大小时，默认to区要永远空着，所以可用为9m

        /*Heap   堆                          总可用         使用量(启动项目时会创建一些必须的类、对象，放在伊甸园)
        def new generation   新生代（幸存区）  total 9216K, used 2510K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
            eden space 8192K,   伊甸园   30% used [0x00000000fec00000, 0x00000000fee738c8, 0x00000000ff400000)
            from space 1024K,   from区    0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
            to   space 1024K,   to区   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
        tenured generation    老年代   total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
            the space 10240K,   0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000)
        Metaspace     元空间     used 3378K, capacity 4496K, committed 4864K, reserved 1056768K
            class space    used 371K, capacity 388K, committed 512K, reserved 1048576K*/
    }

    /**
     * [GC (Allocation Failure) [DefNew: 2182K->677K(9216K), 0.0077801 secs] 2182K->677K(19456K), 0.0087956 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
     * GC: 判断是那种类型GC: 老年代开头为full GC ，新生代只有GC
     * DefNew : 表示发生在新生代。  后面是：回收前的内存占用  回收后的占用  （总容量）
     */
    public static void defNewTest() {
        //强引用，避免被回收
        ArrayList<byte[]> list = new ArrayList<>();
        list.add(new byte[_7M]);
        //发生垃圾回收后可看到一些对象被放入from区，其实是在GC时，放入to区,然后交换from和to的地址（to要永远为空）

        /*[GC (Allocation Failure) [DefNew: 2014K->672K(9216K), 0.0060244 secs] 2014K->672K(19456K), 0.0061430 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
        Heap
        def new generation   total 9216K, used 8414K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
        eden space 8192K,  94% used [0x00000000fec00000, 0x00000000ff38f7a0, 0x00000000ff400000)
        from space 1024K,  65% used [0x00000000ff500000, 0x00000000ff5a8178, 0x00000000ff600000)
        to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
        tenured generation   total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
        the space 10240K,   0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000)*/
    }

    public static void generationTest() {
        ArrayList<byte[]> list = new ArrayList<>();
        list.add(new byte[_7M]);
        list.add(new byte[_512K]);
        //执行第二次垃圾回收(Minor GC)，幸存区内存实在不足的时候，一部分对象晋升至老年代。如果老年代也内存不足，则触发full GC
        list.add(new byte[_512K]);
    }

    /**
     * 垃圾回收对大对象的策略
     * 当对象过大，新生代无法放下对象时，如果老年代空间足够，会将大对象直接放入老年代，且不会有GC
     */
    public static void bigObjectTactics() throws InterruptedException {
        //如果在子线程发生内存溢出，会抛出异常。但是不会使主线程结束。
        new Thread(() -> {
            List<byte[]> list = new ArrayList<>();
            list.add(new byte[_8M]);
            //如果在添加一个大对象，此时新生代老年代都无法放下，会报出 OutOfMemoryError内存溢出。有GC Root根对象引用，不能被垃圾回收
            list.add(new byte[_8M]);
        }).start();
        System.out.println("sleep-----");
        Thread.sleep(1000L);
    }

}
